using System;
using System.Collections.Generic;
using System.Text;

namespace Zsa.Emulation.Render
{
    /// <summary>
    /// Base class for all render devices.
    /// </summary>
    public abstract class RenderDevice < TCaps, TDeviceSettings, TRenderBase > : IDisposable
        where TDeviceSettings : RenderDeviceSettings<TCaps, TDeviceSettings, TRenderBase>
        where TCaps : RenderDeviceCapabilities<TCaps, TDeviceSettings, TRenderBase>
        where TRenderBase : RenderDevice<TCaps, TDeviceSettings, TRenderBase>
    {
        #region Fields
        /// <summary>
        /// The device type.
        /// </summary>
        private readonly RenderDeviceType _DeviceType;
        /// <summary>
        /// The device settings.
        /// </summary>
        private TDeviceSettings _DeviceSettings;
        /// <summary>
        /// True if is disposed.
        /// </summary>
        private bool _IsDisposed = false;
        /// <summary>
        /// The surface control.
        /// </summary>
        protected SurfaceAreaControl _SurfaceControl;
        /// <summary>
        /// The nes machine.
        /// </summary>
        protected NesMachineHost _NesMachineHost;
        #endregion Fields

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="T:RenderDevice"/> class.
        /// </summary>
        /// <param name="deviceType">Type of the device.</param>
        protected internal RenderDevice(TDeviceSettings settings)
        {
            _DeviceSettings = settings;
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="T:Zsa.Emulation.Render.RenderDevice"/> is reclaimed by garbage collection.
        /// </summary>
        ~RenderDevice()
        {
            if (_IsDisposed)
            {
                return;
            }
            _IsDisposed = true;
            OnDispose();
        }
        #endregion Constructor

        #region Methods

        /// <summary>
        /// Initialize the device.
        /// </summary>
        public void InitDevice()
        {
            OnRecreateDevice();
        }

        /// <summary>
        /// Creates or recreates the device.
        /// </summary>
        protected abstract void OnRecreateDevice();

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (_IsDisposed)
            {
                return;
            }
            _IsDisposed = true;
            OnDispose();
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Called when [dispose].
        /// </summary>
        protected abstract void OnDispose();

        /// <summary>
        /// When a surface is attached.
        /// </summary>
        protected virtual void OnSurfaceAttached(SurfaceAreaControl newValue)
        {
        }

        protected virtual void OnMachineAttached()
        {
        }
        #endregion Methods

        #region Properties
        /// <summary>
        /// Gets the render settings.
        /// </summary>
        public virtual TDeviceSettings DeviceSettings
        {
            get
            {
                return _DeviceSettings;
            }
            set
            {
                _DeviceSettings = value;
            }
        }

        /// <summary>
        /// Gets or sets the nes host.
        /// </summary>
        /// <value>The nes host.</value>
        internal NesMachineHost NesHost
        {
            get
            {
                return _NesMachineHost;
            }
            set
            {
                _NesMachineHost = value;
                OnMachineAttached();
            }
        }

        /// <summary>
        /// Gets or sets the surface control.
        /// </summary>
        /// <value>The surface control.</value>
        internal virtual SurfaceAreaControl SurfaceControl
        {
            get {
                return _SurfaceControl;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("surfaceAreaControl");
                }
                OnSurfaceAttached(value);
                _SurfaceControl = value;
            }
        }

        #endregion Properties
    }
}
